package uk.ac.ebi.microarray.expressionprofiler.utils;

import javax.resource.ResourceException;

import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.vfs.FileSystemException;

import uk.ac.ebi.microarray.expressionprofiler.utils.functional.Functor;

/**
 * <p>This class provides <code>execute</code> function which allows repeatable
 * execution of <code>Functor&lt;T&gt;.execute()</code> method.</p>
 * @param &lt;T&gt; Java type of return value of <code>execute</code> function.
 * @author Larionov, Konstantin
 * 
 */
public class RepeatableExecutor<T> {

    /**
     * Executes <code>Functor&lt;T&gt;.execute()</code> method several times.
     * Number of consecutive executions is determined by <code>times</code> parameter.
     * <P>This function should be used in order to perform consecutive executions of
     * methods which may throw <code>FileSystemException</code> in rare cases.
     * Consecutive execution and delay between consecutive tries allows to avoid
     * exception escalation. 
     * @param f <code>Functor&lt;T&gt;</code> interface instance.
     * @param times Number of consecutive executions.
     * @param delay Defines how many milliseconds currently executin thread should sleep between tries.
     * @return Value, returned by <code>Functor&lt;T&gt;.execute()</code> method.
     * @throws ResourceException
     * 
     */
    //TODO: (K. Larionov) Make this method generic (currently only FileSystemException is handled as needed). 
    public static <T> T execute(Functor<T> f, int times, int delay) throws ResourceException {
        int triesCount = times;
        T result = null;

        while (triesCount > 0) {
            try {
                result = f.execute();
            } catch (FileSystemException e) {
                triesCount--;

                if (triesCount == 0)
                    throw new ResourceException(e);

                try {
                    Thread.sleep(delay + (RandomUtils.nextLong() % delay));
                } catch (InterruptedException e1) {
                    throw new ResourceException(e1);
                }
                continue;
            } catch (Exception e) {
                throw new ResourceException(e);
            }
            break;
        }

        return result;
    }
}
